package com.tbit.uqbike.client.controller;

import cn.hutool.core.date.DateUtil;
import com.tbit.common.enums.FeeDayTypeEnum;
import com.tbit.common.enums.FeeTypeEnum;
import com.tbit.uqbike.client.constant.AccountConfigExtConstant;
import com.tbit.uqbike.client.constant.MemberCardConstant;
import com.tbit.uqbike.client.constant.ResponseCode;
import com.tbit.uqbike.client.constant.TimeFeeConstant;
import com.tbit.uqbike.client.dao.core.AccountConfigExtDao;
import com.tbit.uqbike.client.dao.core.AdAccountFeeDao;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.Account;
import com.tbit.uqbike.client.pojo.AdAccountFee;
import com.tbit.uqbike.client.pojo.Machine;
import com.tbit.uqbike.client.pojo.User;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.newEntity.FeeType;
import com.tbit.uqbike.client.pojo.newEntity.TimeFee;
import com.tbit.uqbike.client.pojo.newEntity.TimeFeeRes;
import com.tbit.uqbike.client.pojo.vo.FeeCommon;
import com.tbit.uqbike.client.pojo.vo.VipLogDot;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.Assert;
import com.tbit.uqbike.client.util.DateTimeUtil;
import com.tbit.uqbike.client.util.HolidayUtils;
import com.tbit.uqbike.client.util.OutputUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.DayOfWeek;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 平台费用定义
 *
 * @author Leon
 * 2017年2月24日 下午5:51:05
 */
@Controller
@RequestMapping("/accountFee")
public class AccountFeeController {
    @Autowired
    private MachineService machineService;
    @Autowired
    private AdAccountFeeService adAccountFeeService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private VipCardLogService vipCardLogService;
    @Autowired
    private VipFeeService vipFeeService;
    @Autowired
    private CacheService cacheService;
    @Resource
    private AdAccountFeeDao adAccountFeeDao;
    @Resource
    private AccountConfigExtDao accountConfigExtDao;
    @Resource
    private MachineFeeService machineFeeService;

    /**
     * 获取平台费用信息
     *
     * @param token     令牌
     * @param accountId 帐户id
     * @param session   会话
     * @param response  响应
     * @ignoreParams request
     */
    @RequestMapping("/getByAccountId")
    public void getByAccountId(String token, Integer accountId, HttpSession session,
                               HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {

            Assert.notNull(accountId, "-10001", "区域id不能为空");

            AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(accountId, null);
            addAccountName(adAccountFee);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, adAccountFee);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 根据设备编号获取平台费用
     *
     * @param token     令牌
     * @param machineNO 设备编号
     * @param session   会话
     * @param response  响应
     * @ignoreParams request
     */
    @RequestMapping("/getByMachineNO")
    public void getByMachineNO(String token, String machineNO, HttpSession session,
                               HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(machineNO, "-30001", "machineNO不能为空");

            Machine machine = machineService.getByUserCode(machineNO);
            if (machine == null) {
                throw new BaseException("-30005", "该编号不存在");
            }

            //AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
            AdAccountFee adAccountFee = adAccountFeeService.getFee(machine.getAccountId(), machine.getMachineId(), DateUtil.formatDateTime(new Date()));
            User user = tokenService.getUser(token);
            AdAccountFee vipFee = null;
            Integer feeId = 0;
            if (adAccountFee != null && adAccountFee.getName() != null) {
                feeId = adAccountFee.getFeeId();
            }
            /**查询用户购买的会员卡*/
            VipLogDot vipLogDot = vipCardLogService.getUserVip(machine.getAccountId(), user.getUserId(), feeId);

            if (vipLogDot != null && vipLogDot.getVipType().equals(MemberCardConstant.VIP_CARD_VIPFEE)) {
                //vipFee = vipFeeService.getByVipId(vipLogDot.getVipId());
                vipFee = adAccountFeeService.getVipFee(machine.getAccountId(),DateUtil.formatDateTime(new Date()),vipLogDot.getVipId());
            }
            if (vipFee != null) {
                adAccountFee = vipFee;
            }
            addAccountName(adAccountFee);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, adAccountFee);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取用户费用情况
     * @param token token
     * @param userCode 车辆编号
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/getFeeInfo")
    public void getFeeInfo(String token, String userCode, HttpSession session,
                               HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.hasLength(userCode, "-30001", "machineNO不能为空");

            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "该编号不存在");
            }
            User user = tokenService.getUser(token);
            List<FeeCommon> feeCommons= adAccountFeeService.getTimeFeeByAccountId(machine,user.getUserId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, feeCommons);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取时间费用
     * @param token 令牌
     * @param accountId 区域id
     * @param type 计费类型 0 区域计费、1 车辆计费、2 会员卡计费
     * @param attachId 附加id 车辆计费+会员卡计费需要传递此字段
     * @param request 请求
     * @param response 响应
     */
    @RequestMapping("/getTimeFee")
    public void getTimeFee(String token, Integer accountId, @RequestParam(defaultValue = "0") Integer type, Integer attachId, HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> map = new HashMap<String, Object>();
        try {

            Assert.notNull(accountId, "-10001", "区域id不能为空");

            List<TimeFee> timeFee = adAccountFeeService.getTimeFee(accountId, type, attachId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, timeFee);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 添加区域名称
     *
     * @param adAccountFee
     */
    private void addAccountName(AdAccountFee adAccountFee) {
        if (adAccountFee != null) {
            Account account = cacheService.getAccount(adAccountFee.getAccountId());
            adAccountFee.setAccountName(account == null ? "" : account.getName());
        }
    }

    /**
     * 判断当前区域有哪几个计费模式 ， 当前命中的是哪个计费模式配置的规则
     * @param accountId 区域id
     * @param request 请求
     * @param response 响应
     */
    @GetMapping("/getFeeType")
    public void getFeeType(Integer accountId, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(accountId, "-10001", "区域id不能为空");
            List<FeeType> res = new ArrayList<>();

            FeeTypeEnum feeTypeEnum = cacheService.queryFeeType(accountId);
            if (Objects.equals(feeTypeEnum,FeeTypeEnum.HOLIDAY_FEE)){
                // 判断假日 和 周末
                AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(accountId, AccountConfigExtConstant.HOLIDAY_FEE_SWITCH);
                boolean holidayModel = Objects.nonNull(accountConfigExt) && Objects.equals(accountConfigExt.getParamValue(), "1");
                String nowTime = DateTimeUtil.getNowTime();
                String day = HolidayUtils.getDateValue(nowTime,"day");
                if (holidayModel) {
                    List<TimeFee> allTimeFees = cacheService.getTimeFeeList(accountId);
                    List<TimeFee> weekendFees = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_WEEKEND_FEE, TimeFeeConstant.HOLIDAY_WEEKEND_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
                    if (!weekendFees.isEmpty()) {
                        String dayOfWeek = HolidayUtils.getDateValue(nowTime,"dayOfWeek");
                        boolean isWeekend = Objects.equals(dayOfWeek, String.valueOf(DayOfWeek.SATURDAY)) || Objects.equals(dayOfWeek, String.valueOf(DayOfWeek.SUNDAY));
                        boolean workOnWeekend = HolidayUtils.validWorkOnWeekend(day);
                        // 非调休加班 ， 是周末 ， 命中计费规则
                        boolean valid = !workOnWeekend && isWeekend && validFees(weekendFees,nowTime);
                        res.add(new FeeType(FeeDayTypeEnum.WEEKEND.toString(),valid));
                    }
                    List<TimeFee> holidayFees = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_FEE, TimeFeeConstant.HOLIDAY_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
                    if (!holidayFees.isEmpty()) {
                        // 是假日 且 命中计费规则
                        boolean valid = HolidayUtils.validHoliday(day) && validFees(holidayFees, nowTime);
                        res.add(new FeeType(FeeDayTypeEnum.HOLIDAY.toString(),valid));
                    }
                }
                res.add(new FeeType(FeeDayTypeEnum.WORKDAY.toString(),true));
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, res);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 判断是否命中计费规则
     * @param timeFees 计费规则时段列表
     * @param dateTime 时间
     * @return 是否命中
     */
    private boolean validFees(List<TimeFee> timeFees,String dateTime){
        Integer hour = DateUtil.hour(DateUtil.parse(dateTime),true);
        for(TimeFee timeFee:timeFees){
            if(timeFee.getBeginTime() <= hour && hour < timeFee.getEndTime()){
                return true;
            }
        }
        return false;
    }

    /**
     * 查询计费规则 页面
     * @param accountId 区域id
     * @param feeDayType 计费规则类型
     * @param request 请求
     * @param response 响应
     */
    @GetMapping("/getFeeList")
    public void getFeeList(Integer accountId, @RequestParam(defaultValue = "WORKDAY") String feeDayType, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(accountId, "-10001", "区域id不能为空");
            // 假日计费是否启用判断
            // 当前时间
            String nowTime = DateTimeUtil.getNowTime();
            Integer hour = DateUtil.hour(DateUtil.parse(nowTime),true);

            List<TimeFee> res = new ArrayList<>();
            List<TimeFee> timeFeeList = cacheService.getTimeFeeList(accountId);
            if (Objects.equals(FeeDayTypeEnum.WORKDAY.toString(),feeDayType)){
                // 普通区域计费
                List<TimeFee> feeList = timeFeeList.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_AREA_FEE, TimeFeeConstant.HOLIDAY_AREA_TIME_FEE).contains(x.getType())).collect(Collectors.toList());
                validNow(feeList,hour);
                res.addAll(feeList);
            }if (Objects.equals(FeeDayTypeEnum.WEEKEND.toString(),feeDayType)){
                // 周末默认计费 / 分时计费
                List<TimeFee> feeList = timeFeeList.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_WEEKEND_FEE, TimeFeeConstant.HOLIDAY_WEEKEND_TIME_FEE).contains(x.getType())).collect(Collectors.toList());
                validNow(feeList,hour);
                res.addAll(feeList);
            }if (Objects.equals(FeeDayTypeEnum.HOLIDAY.toString(),feeDayType)){
                // 假日默认计费 / 分时计费
                List<TimeFee> feeList = timeFeeList.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_FEE, TimeFeeConstant.HOLIDAY_TIME_FEE).contains(x.getType())).collect(Collectors.toList());
                validNow(feeList,hour);
                res.addAll(feeList);
            }
            // 车辆计费
//            AdAccountFee machineFee = machineFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, res);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 标识生效的计费时间段
     * @param timeFees 计费时间段列表
     * @param hour 骑行起始时间
     */
    private void validNow(List<TimeFee> timeFees, Integer hour) {
        List<TimeFee> feeList = timeFees.stream().sorted(Comparator.comparingInt(TimeFee::getType).reversed()).collect(Collectors.toList());
        boolean flag = false;
        for (TimeFee timeFee : feeList) {
            if (!flag && timeFee.getBeginTime() <= hour && hour < timeFee.getEndTime()) {
                timeFee.setValidNow(true);
                flag = true;
            }else {
                timeFee.setValidNow(false);
            }
        }
    }
}